var InputHandlers = {};

var KeyboardInput = Base.extend({
    initialize: function initialize() {
        this.keys = {
            forward: false,
            backward: false,
            left: false,
            right: false,
            lookLeft: false,
            lookRight: false,
            run: false,
            sidestep: false,
            up: false,
            down: false,
            enableGPS: false
        };
        this.enabled = false;
    },
    isAvailable: function isAvailable() {
        return true;
    },
    enable: function enable() {
        //http://www.webonweboff.com/tips/js/event_key_codes.aspx
        if (!this.enabled) {
            this.enabled = true;
            var self = this;
            $(window).on('keyup.keyboard-input', function (event) {
                var prevent = true;
                switch (event.which) {
                    case 38: //up-arrow
                    case 87: //w
                        self.keys.forward = false;
                        break;
                    case 83: //s
                    case 40: //down-arrow
                        self.keys.backward = false;
                        break;
                    case 65: //a
                        self.keys.left = false;
                        break;
                    case 68: //d
                        self.keys.right = false;
                        break;
                    case 37: //left-arrow
                        self.keys.lookLeft = false;
                        break;
                    case 39: //right-arrow
                        self.keys.lookRight = false;
                        break;
                    case 32: //Spacebar
                        self.keys.up = false;
                        break;
                    case 67: //c
                        self.keys.down = false;
                        break;
                    case 16: //Shift
                        self.keys.run = false;
                        break;
                    case 17: //Control
                        self.keys.sidestep = false;
                        break;
                    case 71: //g
                        self.keys.enableGPS = false;
                        break;
                    default:
                        prevent = false;
                        break;
                }
                if (prevent) event.preventDefault();
            });

            $(window).on('keydown.keyboard-input', function (event) {
                var prevent = true;
                switch (event.which) {
                    case 38: //up-arrow
                    case 87: //w
                        self.keys.forward = true;
                        break;
                    case 83: //s
                    case 40: //down-arrow
                        self.keys.backward = true;
                        break;
                    case 65: //a
                        self.keys.left = true;
                        break;
                    case 68: //d
                        self.keys.right = true;
                        break;
                    case 37: //left-arrow
                        self.keys.lookLeft = true;
                        break;
                    case 39: //right-arrow
                        self.keys.lookRight = true;
                        break;
                    case 32: //Spacebar
                        self.keys.up = true;
                        break;
                    case 67: //c
                        self.keys.down = true;
                        break;
                    case 16: //Shift
                        self.keys.run = true;
                        break;
                    case 17: //Control
                        self.keys.sidestep = true;
                        break;
                    case 71: //g
                        self.keys.enableGPS = true;
                        break;
                    default:
                        prevent = true;
                        break;
                }
                if (prevent) event.preventDefault();
            });
        }
    },
    disable: function disable() {
        if (this.enabled) {
            this.enabled = false;
            $(window).off('keyup.keyboard-input');
            $(window).off('keydown.keyboard-input');
        }
    },
    update: function update(context) {
        var camera = context.camera;
        var movement = this.keys.run ? 6 : 2;
        if (this.keys.forward) camera.move(movement);
        else if (this.keys.backward) camera.move(-1 * movement);
        if (this.keys.sidestep) {
            if (this.keys.left) camera.sidestep(-1 * movement);
            else if (this.keys.right) camera.sidestep(movement);
        }
        else {
            if (this.keys.left) camera.turn(util.radians(3));
            else if (this.keys.right) camera.turn(util.radians(-3));
        }
        if (!InputHandlers.Compass.enabled) {
            if (this.keys.lookLeft) camera.turnLineOfSight(util.radians(3));
            else if (this.keys.lookRight) camera.turnLineOfSight(util.radians(-3));
            else if (camera.lineOfSight != camera.bearing) camera.normalizeLineOfSightToBearing(util.radians(10));
        }
        if (this.keys.up) camera.z++;
        else if (this.keys.down) if (camera.z + 1 >= 0) camera.z--;

        if (this.keys.enableGPS) {
            if (InputHandlers.Gps.enabled) {
                context.camera.lineOfSightIndependent = false;
                InputHandlers.Gps.disable();
                InputHandlers.Compass.disable();
            }
            else {
                context.camera.lineOfSightIndependent = true;
                InputHandlers.Gps.enable();
                InputHandlers.Compass.enable();
            }
        }
    }
});


var TapInput = Base.extend({
    initialize: function initialize() {
        this.moveBuffer = 0;
        this.x = 0;
        this.y = 0;
        this.enabled = false;
    },
    isAvailable: function isAvailable() {
        return true;
    },
    enable: function enable() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;
            $(window).on('vclick.touch-input', function (event) {
                var prevent = true;
                self.x = event.clientX;
                self.y = event.clientY;
                self.moveBuffer = 20;
                if (prevent) event.preventDefault();
            });
        }
    },
    disable: function disable() {
        if (this.enabled) {
            this.enabled = false;
            $(window).off('vclick.touch-input');
        }
    },
    update: function update(context) {
        var camera = context.camera;

        if (this.moveBuffer > 0) {
            var xm = context.viewPort.size.w / 7;
            var ym = context.viewPort.size.h / 5;
            var yv = 1;

            if (this.y > ym * 4) yv = -1;
            else if (this.y > ym * 3) yv = -0.5;
            else if (this.y > ym * 2) yv = 0.5;

            if (this.x < xm) camera.turn(util.radians(2));
            else if (this.x < xm * 3) camera.turn(util.radians(0.5));
            else if (this.x > (xm * 6)) camera.turn(util.radians(-2));
            else if (this.x > (xm * 4)) camera.turn(util.radians(-0.5));

            if (this.moveBuffer > 12) camera.move(5 * yv);
            else if (this.moveBuffer > 10) camera.move(3 * yv);
            else if (this.moveBuffer > 6) camera.move(2 * yv);
            else camera.move(1 * yv);
            this.moveBuffer--;
        }
    }
});

var GpsInput = Base.extend({
    initialize: function initialize(firstCoordinateAquiredCallback) {
        this.geo = null;
        this.isInitialized = false;
        this.lastUpdate = null;
        this.timeSinceLastUpdate = null;
        this.enabled = false;
        this.timerHandle = null;
        this.timerInterval = 1000;
        this.useSampleData = false;
        this.sampleFeed = [
            { lat: 39.955813, lon: -86.011362 },
            { lat: 39.960744, lon: -86.01546 },
            { lat: 39.960512, lon: -86.02856 },
            { lat: 39.956142, lon: -86.039257 },
            { lat: 39.970549, lon: -86.034107 },
            { lat: 39.971273, lon: -86.004324 }];
        this.sampleFeedIndex = 0;
        this.firstCoordinateAquired = firstCoordinateAquiredCallback;
    },
    isAvailable: function isAvailable() {
        return (self.useSampleData || ('geolocation' in navigator));
    },
    nextSample: function nextSample(event) {
        var location = this.sampleFeed[this.sampleFeedIndex];
        this.sampleFeedIndex++;
        if (this.sampleFeedIndex >= this.sampleFeed.length)
            this.sampleFeedIndex = 0;

        this.geo = GeoCoordinates.newInstance(location.lat, location.lon);
        this.gpsReadingAcquired();
    },
    gpsReadingAcquired: function gpsReadingAcquired() {
        var time = new Date().getTime();
        if (this.lastUpdate != null) {
            this.timeSinceLastUpdate = time - this.lastUpdate;
            this.lastUpdate = time;
        }
        else {
            this.timeSinceLastUpdate = -1;
            this.lastUpdate = time;
        }
        if (!this.isInitialized) {
            this.isInitialized = true;
            if (this.firstCoordinateAquired != null) this.firstCoordinateAquired(this.geo);
        }
    },
    enable: function enable(firstCoordinateAquiredCallback) {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;

            if (firstCoordinateAquiredCallback != null)
                self.firstCoordinateAquired = firstCoordinateAquiredCallback;

            if (self.useSampleData) {
                self.nextSample();
                self.timerHandle = setInterval(function (event) {
                    self.nextSample(event);
                }, self.timerInterval);
            }
            else {
                self.timerHandle = setInterval(function (event) {
                    navigator.geolocation.getCurrentPosition(
                        function (position) { // on success
                            if (position != null && ("coords" in position)
                                && !isNaN(position.coords.latitude) && !isNaN(position.coords.longitude)) {
                                self.geo = GeoCoordinates.newInstance(position.coords.latitude, position.coords.longitude);
                                self.gpsReadingAcquired();
                            }
                        },
                        function (error) {
                            if (!self.isInitialized) {
                                self.isInitialized = true;
                                if (self.geo == null) self.geo = GeoCoordinates.newInstance(39.955959, -86.013181);
                                if (self.firstCoordinateAquired != null) self.firstCoordinateAquired(self.geo);
                                self.timeSinceLastUpdate = -1;
                                self.lastUpdate = new Date().getTime();
                            }
                        }, // on error
                        { maximumAge: 60000, timeout: 5000, enableHighAccuracy: false }); // options
                }, self.timerInterval);
            }
        }
    },
    disable: function disable() {
        if (this.enabled) {
            this.enabled = false;
            clearTimeout(this.timerHandle);
        }
    },
    update: function update(context) {
        if (this.enabled && this.geo != null) {
            this.geo.updateLocation(context);
            var l = this.geo.locationRelativeToCamera,
                p = l.placement,
                d = Math.abs(l.distance),
                a = p.angleFromBearing;
            if (d > 3 && !isNaN(a)) {
                a += context.camera.bearing;
                while (a > util._360) a -= util._360;
                context.camera.normalizeBearingToAngle(a, util.radians(10));
                /*if (this.lastGeo != null) {
                    if (this.lastGeo.x != this.geo.x || this.lastGeo.y != this.geo.y) {
                        context.camera.normalizeToAngle(false, null, a);
                    }
                }
                else
                    context.camera.normalizeToAngle(false, null, a);
                this.lastGeo = this.geo;*/

                var absD = Math.abs(l.distance);
                if (absD > 0) {
                    var speed = 1;
                    if (absD > 100) speed = 35;
                    else if (absD > 80) speed = 25;
                    else if (absD > 60) speed = 18;
                    else if (absD > 20) speed = 12;
                    else if (absD > 10) speed = 6;
                    else if (absD > 5) speed = 2;

                    context.camera.move(speed);
                }
                else {
                }
            }
        }
        //TODO:  calculate distance and angle to location, then based on distance and angle, start moving.
        //       if already within a short distance of the point, slow down and wander, or just stop
    }
});

var CompassInput = Base.extend({
    initialize: function initialize() {
        this.compassBearing = null; // north
        this.lastUpdate = null;
        this.timeSinceLastUpdate = null;

        var self = this;
        this.handler = function (event) {
            if (!isNaN(event.alpha)) {
                var degrees = event.alpha + 270.0;
                if (degrees > 360) degrees -= 360;
                self.compassBearing = util.radians(degrees); // 0 is North, which may be a problem.. we shall see.
                var time = new Date().getTime();
                if (self.lastUpdate != null) {
                    self.timeSinceLastUpdate = time - self.lastUpdate;
                    self.lastUpdate = time;
                }
                else {
                    self.timeSinceLastUpdate = -1;
                    self.lastUpdate = time;
                    if (self.firstCoordinateAquired != null) self.firstCoordinateAquired(self.geo);
                }
            }
        };
    },
    getBearingDegrees: function getBearingDegrees() {
        return util.degrees(this.compassBearing);
    },
    isAvailable: function isAvailable() {
        return (window.DeviceOrientationEvent) ? true : false;
    },
    enable: function enable() {
        if (!this.enabled) {
            this.enabled = true;
            window.addEventListener("deviceorientation", this.handler, false);
        }
    },
    disable: function disable() {
        if (this.enabled) {
            this.enabled = false;
            window.removeEventListener("deviceorientation", this.handler, false);
        }
    },
    update: function update(context) {
        if (this.enabled && this.compassBearing != null && !isNaN(this.compassBearing)) {
            context.camera.lineOfSightIndependent = true;
            context.camera.normalizeLineOfSightToAngle(this.compassBearing, util.radians(10));
            //context.camera.lineOfSight = this.compassBearing;
        }
    }
});

InputHandlers.Gps = GpsInput.newInstance();
InputHandlers.Compass = CompassInput.newInstance();
InputHandlers.Keyboard = KeyboardInput.newInstance();
InputHandlers.Tap = TapInput.newInstance();

// This is still a work in progress
/*var SimpleMouseInput = Base.extend({
    initialize: function initialize() {
        //InputBase.initialize.call(this);
        this.commands = {
            left: false,
            middle: false,
            right: false
        };
        this.enabled = false;
    },
    isAvailable: function isAvailable() {
        return true;
    },
    enable: function enable() {
        if (!this.enabled) {
            this.enabled = true;
            var self = this;
            $(window).on('mousedown.mouse-input', function (event) {
                var prevent = true;
                switch (event.which) {
                    case 1: //left
                        self.commands.left = true;
                        break;
                    case 2: //middle
                        self.commands.middle = true;
                        break;
                    case 3: //right
                        self.commands.right = true;
                        break;
                    default:
                        prevent = false;
                        break;
                }
                if (prevent) event.preventDefault();
            });

            $(window).on('mouseup.mouse-input', function (event) {
                var prevent = true;
                switch (event.which) {
                    case 1: //left
                        self.commands.left = false;
                        break;
                    case 2: //middle
                        self.commands.middle = false;
                        break;
                    case 3: //right
                        self.commands.right = false;
                        break;
                    default:
                        prevent = false;
                        break;
                }
                if (prevent) event.preventDefault();
            });
        }
    },
    disable: function disable() {
        if (this.enabled) {
            this.enabled = false;
            $(window).off('mouseup.mouse-input');
            $(window).off('mousedown.mouse-input');
        }
    },
    update: function update(context) {
        var camera = context.camera;
        var movement = 2;
        if (this.commands.left) camera.move(movement);
        else if (this.commands.right) camera.move(-1 * movement);
    }
});*/